/*
 * SystemView.cpp
 *
 *  Created on: Dec 16, 2011
 *      Author: Netali
 */

#include <set>
#include <map>
#include <fstream>
#include <iostream>
#include <stdlib.h>
#include <String>
#include <math.h>
#include "SystemView.h"

int extentCounter = 0;


SystemView::SystemView () {
	time_slice_size = 3600;
	nof_time_slices = 0;
	my_entries = 0;
	allDevices = new set<DeviceData*>();
	my_luns = new map<LUNID, LunTotalStatistics*> ();
	allocation = new SystemAllocation(); 
	allExtents = new map<LUNID, map<__int64, Extent*>*> (); 
	time_correlation = 0;
}

void SystemView::increaseTimeSlices(){
	nof_time_slices++;
}

void SystemView::addEntry (ExtentEntry* e) {
	my_entries->insert(e);
}

int SystemView::getTimeSliceSize(){
	return time_slice_size;
}

set<DeviceData*>* SystemView::getAllDevices(){
	return allDevices;
}

std::set<ExtentEntry*>* SystemView::getMy_entries() {
	return my_entries;
}

std::map<LUNID, LunTotalStatistics*>* SystemView::getMy_luns(){
	return my_luns;
}

void SystemView::addLun (pair<const LUNID,LunTotalStatistics*> l){
	my_luns->insert(l);
}

LunTotalStatistics* SystemView::getLunTotalStatistics(const LUNID id){
	map<LUNID,LunTotalStatistics*>::iterator it =  my_luns->find(id);
	if(it != my_luns->end())
	   //element found;
		return (*my_luns)[id];
	return NULL;
}

void SystemView::updateLuns(LUNID lId,ExtentEntry* entry){
        LunTotalStatistics* total;
		Lun* l;
        int time = entry->my_time;
        ExtentStatistics* extentStat = entry->my_stat;
        
        if (my_luns->find(lId) != my_luns->end()){//lun exist already
                total = my_luns->at(lId);// get lun total statistics
				l = total->getLun();
				entry->getMy_extent()->setLun(l);
                LunTimeStatistics* lunStat = total->getLunTimeStatistics(time);
                if (lunStat != 0)//lun time statistics exist
                        lunStat->addStatistics(extentStat->statistics);
                else// lun time statistics don't exist
                {
                        lunStat = new LunTimeStatistics(lId,time);
                        lunStat->addStatistics(extentStat->statistics);
                        total->addLunTimeStatistics(lunStat);
                }
        }
        else{//lun doesn't exist
                l = new Lun(lId,.4,.3,.3,1);//creates new lun
				entry->getMy_extent()->setLun(l);
                LunTimeStatistics* lunStat = new LunTimeStatistics(lId,time);//creates new lun time statistics
                lunStat->addStatistics(extentStat->statistics);
                total = new LunTotalStatistics(l);//creates new lun total statistics
                total->addLunTimeStatistics(lunStat);
                my_luns->insert(pair<LUNID, LunTotalStatistics*>(lId,total));
				allocation->setRandomResponseTime(l);
        }
}

void SystemView::addDevice(DeviceData* d){
	allDevices->insert(d);
}


DeviceData* SystemView::getDevice(string type){
	set<DeviceData*>::iterator deviceIter = allDevices->begin();
	while(deviceIter!=allDevices->end()){
		if ((*deviceIter)->getKind().compare(type) == 0){
			return (*deviceIter);
		}
		deviceIter++;
	}
	return NULL;
}

void SystemView::divide_all_lun_priorities(int i){

	map<LUNID, LunTotalStatistics*>::iterator lun_it;

	for (lun_it = my_luns->begin(); lun_it != my_luns->end(); lun_it++){
		
		if (i == 1){
			lun_it->second->getLun()->divide_priority();
		}
		if (i == 0) {
			lun_it->second->getLun()->setPriority(1);
		}
		if (i==2){
			lun_it->second->getLun()->divide_priority_3_groups();
		}
	}

}

void SystemView::divide_system(double ssdnum, double fcnum, double satanum){
	allocation->setSSD_size(getDevice("SSD"),ssdnum);
	allocation->setFC_size(getDevice("FC"),fcnum);
	allocation->setSATA_size(getDevice("SATA"),satanum);
}

Extent* SystemView::getExtent(LUNID id, __int64 offset){
	map<LUNID, map<__int64,Extent*>*>::iterator lun = allExtents->find(id);
	map<__int64, Extent*>::iterator offset_it;
	Extent* e;
	// new lun
	if (lun == allExtents->end()){
		extentCounter++;
		e = new Extent(offset);
		map<__int64, Extent*>* new_m = new map<__int64, Extent*>();
		new_m->insert(pair<__int64, Extent*>(offset, e));
		allExtents->insert(pair<LUNID, map<__int64,Extent*>*>(id, new_m));
	}
	else {
		offset_it = lun->second->find(offset);
		if (offset_it == lun->second->end()){
			extentCounter++;
			e = new Extent(offset);
			lun->second->insert(pair<__int64,Extent*>(offset, e));
		}
		else {
			e = offset_it->second;
		}
	}
	return e;
}


map<int, double>* SystemView::getTimeCorrOutput(){
	if (time_correlation != 0){
		return time_correlation->getTimeCorr();
	}
	else {
		return 0;
	}
}

void SystemView::startTimeCorrelation(int time){
	time_correlation = new TimeCorrelation(time);
}

void SystemView::stopTimeCorrelation(int time){
	delete time_correlation;
	time_correlation = 0;
}

int SystemView::calculate_time_correlation(int time, list<ExtentStatsAlgo*>* SSD){
	if (time_correlation != 0){
		return time_correlation->calcTimeCorrelation(time, SSD);
	}
	return 0;
}

// destructor
SystemView::~SystemView(void){

	// delete myluns
	map<LUNID, LunTotalStatistics*>::iterator lun_it; 
	for(lun_it = my_luns->begin(); lun_it != my_luns->end(); lun_it++){
		delete (lun_it->second);
	}
	delete my_luns;

	// delete devices
	set<DeviceData*>::iterator device_it;
	for (device_it = allDevices->begin(); device_it != allDevices->end(); device_it++){
		delete (*device_it);
	}
	delete allDevices;

	// delete cache
	for (int i = 0; i < my_cache->nof_partitions; i++){
		delete my_cache->partition_luns[i];
	}
	delete my_cache->partition_luns;
	delete my_cache->partition_sizes;
	delete my_cache;
	
	// delete allocation
	delete allocation;

	// delete allExtents
	map<LUNID,map<__int64, Extent*>*>::iterator lunid_it;
	map<__int64,Extent*>::iterator offset_it;
	for (lunid_it = allExtents->begin(); lunid_it != allExtents->end(); lunid_it++){
		for (offset_it = (*lunid_it).second->begin(); offset_it != (*lunid_it).second->end(); offset_it++){
			delete (*offset_it).second;
		}
		delete (*lunid_it).second;
	}
	delete allExtents;

	// delete time correlation
	delete time_correlation;
}

void SystemView::startOver(void){
	nof_time_slices = 0 ;
	// reset myluns
	map<LUNID, LunTotalStatistics*>::iterator lun_it; 
	for(lun_it = my_luns->begin(); lun_it != my_luns->end(); lun_it++){
		lun_it->second->startOver();
	}
	// delete devices
	set<DeviceData*>::iterator device_it;
	for (device_it = allDevices->begin(); device_it != allDevices->end(); device_it++){
		(*device_it)->startOver();
	}
	
	map<LUNID, map<__int64, Extent*>*>::iterator extent_it;
	map<__int64, Extent*>::iterator extent_it2;
	for (extent_it = allExtents->begin(); extent_it != allExtents->end(); extent_it++){
		for (extent_it2 = extent_it->second->begin(); extent_it2 != extent_it->second->end(); extent_it2++){
			extent_it2->second->setPlace(0);
		}
	}

	// delete time correlation
	delete time_correlation;

}

void SystemView::writeAllocStats(){
	map<double, list<Lun*>*>::iterator groupIter;
	groupIter = allocation->getSystemGroups()->begin();
	while(groupIter!=allocation->getSystemGroups()->end()){
		cout<<"number of luns in group with RT of "<<(*groupIter).first<<" is "<<(*groupIter).second->size()<<endl;
		groupIter++;
	}
}